
use std::io::{Read, Seek};

use cross_rs::IResult;
use cross_rs::fasttime::b2_time::TimeFT;
use cross_rs::memory::bytes_io::BytesIO;
use xz2::read::{XzEncoder, XzDecoder};

use crate::disk::{file_io, path_io};

/// from: xz --help, in ubuntu 22.04
/// -0 ... -9           compression preset; default is 6; take compressor *and* decompressor memory usage into account before using 7-9!
pub const DEFAULT_LEVEL: u32 = 6;

pub fn zip_file(file_raw: &str, level: u32) -> IResult<usize>{
    if file_raw.ends_with(".xz") {
        return Ok(0);
    }
    // let len_raw =bytes_raw.len();
    let bytes= file_io::read_bytes(file_raw)?;
    let bytesio_raw = BytesIO::from_bytes("raw".to_string(), bytes);
    if bytesio_raw.len() == 0 {
        return Ok(0);
    }
    let mut compressor = XzEncoder::new(bytesio_raw, level);
    let mut bts_xz = vec![];
    let len_xz = compressor.read_to_end(&mut bts_xz)?;
    // println!("压缩: {len_xz}, 原始: {len_raw}");
    let file_xz = format!("{file_raw}.xz");
    file_io::write_bytes(&bts_xz, &file_xz)?;
    path_io::remove_file(file_raw);


    Ok(len_xz)
}

pub fn zip_bytes(bytes_raw: Vec<u8>, level: u32) -> IResult<Vec<u8>>{
    // let len_raw =bytes_raw.len();
    let bytesio_raw = BytesIO::from_bytes("raw".to_string(), bytes_raw);
    let mut compressor = XzEncoder::new(bytesio_raw, level);
    let mut bts_xz = vec![];
    compressor.read_to_end(&mut bts_xz)?;

    Ok(bts_xz)
}

pub fn zip_bytes_to_file(bytes_raw: Vec<u8>, file_xz: &str, level: u32) -> IResult<usize>{
    // let len_raw =bytes_raw.len();
    let bytesio_raw = BytesIO::from_bytes("raw".to_string(), bytes_raw);
    let mut compressor = XzEncoder::new(bytesio_raw, level);
    let mut bts_xz = vec![];
    let len_xz = compressor.read_to_end(&mut bts_xz)?;
    // println!("压缩: {len_xz}, 原始: {len_raw}");
    file_io::write_bytes(&bts_xz, file_xz)?;

    Ok(len_xz)
}

pub fn zip_file_to_bytes(file_raw: &str, level: u32) -> IResult<Vec<u8>>{
    let start = TimeFT::now();
    let bytesio_raw = crate::memory::bytesio_from_file("raw".to_string(), file_raw)?;
    let len_raw = bytesio_raw.len();
    let mut compressor = XzEncoder::new(bytesio_raw, level);
    let mut bts_xz = vec![];
    let len_xz = compressor.read_to_end(&mut bts_xz)?;
    let ratio100 = len_xz as f64 * 100_f64 / len_raw as f64;
    let end = TimeFT::now();

    info!("zip_file_to_bytes: {start}, {:.1} secs; zip {ratio100:.1}% from {}(raw) to {}(xz); {file_raw}", end.seconds_from(&start), file_io::size2string(len_raw as i64), file_io::size2string(len_xz as i64));

    Ok(bts_xz)
}

pub fn zip_bytesio_to_file(mut bytesio_raw: BytesIO, file_xz: &str, level: u32) -> IResult<usize>{
    let start = TimeFT::now();
    bytesio_raw.seek(std::io::SeekFrom::Start(0))?;
    let len_raw = bytesio_raw.len();
    let mut compressor = XzEncoder::new(bytesio_raw, level);
    let mut bts_xz = vec![];
    let len_xz = compressor.read_to_end(&mut bts_xz)?;
    let ratio = len_xz as f64 * 100_f64 / len_raw as f64;
    let end = TimeFT::now();
    info!("zip_bytesio_to_file: xzip: {start}, {:.1} secs; 压缩率 {ratio:.1} %: 压缩后 {len_xz}, 原始: {len_raw}, {file_xz}", end.seconds_from(&start));
    file_io::write_bytes(&bts_xz, file_xz)?;

    Ok(len_xz)
}

pub fn zip_bytesio_to_bytes(mut bytesio_raw: BytesIO, level: u32) -> IResult<Vec<u8>>{
    let start = TimeFT::now();
    bytesio_raw.seek(std::io::SeekFrom::Start(0))?;
    let len_raw = bytesio_raw.len();
    let mut compressor = XzEncoder::new(bytesio_raw, level);
    let mut bts_xz = vec![];
    let len_xz = compressor.read_to_end(&mut bts_xz)?;
    let ratio = len_xz as f64 * 100_f64 / len_raw as f64;
    let end = TimeFT::now();
    info!("zip_bytesio_to_file: xzip: {start}, {:.1} secs; 压缩率 {ratio:.1} %: 压缩后 {len_xz}, 原始: {len_raw}", end.seconds_from(&start));

    Ok(bts_xz)
}

pub fn unzip_file_to_bytes(file_xz: &str) -> IResult<Vec<u8>> {
    let bytesio_xz = crate::memory::bytesio_from_file("xz".to_string(), file_xz)?;
    let _len_xz = bytesio_xz.len();

    let mut decoder = XzDecoder::new(bytesio_xz);

    let mut bts_raw = vec![];
    let _len_raw = decoder.read_to_end(&mut bts_raw)?;

    Ok(bts_raw)
}

pub fn unzip_bytes(bts_xz: Vec<u8>) -> IResult<Vec<u8>> {
    let start = TimeFT::now();
    let len_xz = bts_xz.len();
    let bytesio_xz = BytesIO::from_bytes("xz".to_string(), bts_xz);

    let mut decoder = XzDecoder::new(bytesio_xz);

    let mut bts_raw = vec![];
    let len_raw = decoder.read_to_end(&mut bts_raw)?;

    let ratio100 = len_xz as f64 * 100_f64 / len_raw as f64;
    let end = TimeFT::now();

    info!("xunzip bytes: {start}, {:.1} secs; zip {ratio100:.1}% from {}(raw) to {}(xz);", end.seconds_from(&start), file_io::size2string(len_raw as i64), file_io::size2string(len_xz as i64));

    Ok(bts_raw)
}


#[test]
fn test_xz_encode_20230606(){

    let file_raw = "D:/temp/2000721.raw";
    let file_xz = format!("{file_raw}.xz");
    let bytes_raw = file_io::read_bytes(file_raw).unwrap();
    let _len_xz = zip_bytes_to_file(bytes_raw, &file_xz, 9).unwrap();
}

#[test]
fn test_xz_decode_20230606(){
    let file_xz = "D:/temp/2000721.bin.xz";
    
    let _bts_raw = unzip_file_to_bytes(&file_xz).unwrap();

}